package cn.wsn2018.tools.util;

import cn.wsn2018.tools.common.CommonValue;
import cn.wsn2018.tools.dao.LogsInfoDao;
import cn.wsn2018.tools.model.Log.ErrorLogInfo;
import cn.wsn2018.tools.model.Log.LogsInfo;
import cn.wsn2018.tools.model.Log.WarningLogInfo;
import cn.wsn2018.tools.model.SystemConfig;
import cn.wsn2018.tools.service.SecUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


/**
 * Created by NZX on 2018/12/19.
 */
@Component
public class LogUtil {

    private static Logger logger = LoggerFactory.getLogger(LogUtil.class);

    @Autowired
    private LogsInfoDao logsInfoDao;
    @Autowired
    private SecUserService secUserService;
    private static LogUtil logUtil;


    @PostConstruct
    public void init(){
        logUtil = this;
        logUtil.logsInfoDao = this.logsInfoDao;
    }


    //业务日志文件的锁
    static ReentrantLock userLock = new ReentrantLock();
    //告警日志文件的锁
    static ReentrantLock warnLock = new ReentrantLock();
    //错误日志文件的锁
    static ReentrantLock errorLock = new ReentrantLock();

    //三个文件的状态，false表示未备份，true表示正在备份中
    static boolean warnFileState=false;
    static boolean errorFileState=false;
    static boolean userFileState=false;


    //打印日志工具
    public static boolean produceLogs(String username,String type,String info,Integer eventType,String result){

        if(StringUtil.isEmptyString(username)||StringUtil.isEmptyString(type)
        ||StringUtil.isEmptyString(info)||StringUtil.isEmptyString(result)){
            return false;
        }

        LogsInfo logsInfo = new LogsInfo();
        logsInfo.setUsername(Regex.checkReg(username));
        logsInfo.setCreateTime(DateUtil.dateToString(new Date()));
        logsInfo.setOperateInfo(Regex.checkReg(info));
        logsInfo.setResult(Regex.checkReg(result));
        logsInfo.setType(Regex.checkReg(type));
        logsInfo.setEventType(eventType);
        logger.info(logsInfo.toString());
        //写入内存
        if(logUtil==null){
            logUtil=new LogUtil();
        }

        logUtil.writeLog("UserLog.log",logsInfo.toString());

        boolean flag = logUtil.logsInfoDao.saveLogsInfo(logsInfo);
        return flag;
    }

    public static boolean produceErrorLogs(ErrorLogInfo errorLogInfo){
        /**
         * @Description:
         * @Param: [errmessage]
         * @return: boolean
         * @Author: SongDaisong
         * @Date: 14:04
         */

        logger.error(errorLogInfo.toString());

        logUtil.logsInfoDao.saveLogInfo(errorLogInfo,"errorLogInfo");
        //写入内存
        if(logUtil==null){
            logUtil=new LogUtil();
        }

         logUtil.writeLog("ErrorLog.log",errorLogInfo.toString());
        return true;
    }

    public static  boolean produceWarningLogs(String username,String warningType,String warningLevel,
                                              String warningModuleName,String ipAddress, String warningMessage){
        /**
        * @Description: 产生告警日志
        * @Param: [username, warningType, warningLevel, warningModuleName, ipAddress, warningMessage]
        * @return: boolean
        * @Author: SongDaisong
        * @Date:  2019/12/11 16:13
        */
        if(StringUtil.isEmptyString(username)){
            return false;
        }
        WarningLogInfo warningLogInfo = new WarningLogInfo();
        warningLogInfo.setWarningType(warningType);
        warningLogInfo.setWarningLevel(warningLevel);
        warningLogInfo.setIpAddress(ipAddress);
        warningLogInfo.setWarningInfo(warningMessage);
        warningLogInfo.setType(CommonValue.LOG_TYPE_SYS);
        warningLogInfo.setUsername(username);
        warningLogInfo.setWarningModuleName(warningModuleName);
        warningLogInfo.setCreateTime( DateUtil.dateToString(new Date()));
        logger.error(warningLogInfo.toString());
        if(logUtil==null){
            logUtil=new LogUtil();
        }
        logUtil.writeLog("WarnLog.log",warningLogInfo.toString());
        return logUtil.logsInfoDao.saveLogInfo(warningLogInfo,"warningLogInfo");
    }

    public static String produceWarningMsg(String warningType,String username,String role,String moduleName){
        /**
        * @Description: 产生通过api接口进行越权访问的告警信息的msg部分
        * @Param: [warningType, username, role, moduleName]
        * @return: java.lang.String
        * @Author: SongDaisong
        * @Date:  2020/2/4 15:32
        */
        if(CommonValue.WARNING_TYPE_1.equals(warningType)&&!StringUtil.isEmptyString(role)){
            return "用户名为:"+username+"的"+ RoleUtil.getRoleName(role) +",试图越权访问"+moduleName;
        }else{
            return "用户名为:"+username+"尝试异常登录";
        }
    }

    public static String produceUrlWarningMsg(String username,String roleName,String module){
        /**
        * @Description: 产生通过url进行越权访问时的告警信息msg部分
        * @Param: []
        * @return: java.lang.String
        * @Author: SongDaisong
        * @Date:  2020/2/4 15:34
        */
        String msg="用户名为:"+username+"的"+roleName+",尝试直接通过url越权访问"+module;
        return msg;
    }

    /**
     * 日志文件自动备份
     * @param logContent
     */
    private  void  writeLog(String fileName,String logContent){
        /**
        * @Description:  日志文件自动备份
        * @Param: [fileName, logContent]     //fileName:WarnLog.log  ErrorLog.log UserLog.log
        * @return: void
        * @Author: SongDaisong
        * @Date:  2020/4/14 20:59
        */
        //尝试获取锁
        switch (fileName){
            case CommonValue.ERROR_LOG:{
                while (true){
                    if (!errorFileState){
                        //文件没有在进行备份
                        boolean lock = errorLock.tryLock();
                        if (lock){
                            logger.info("lock for ErrorLog.log successfully.");
                            break;
                        }
                    }
                    //获取失败3秒后再去尝试
                    ThreadUtil.sleep(3);
                }
                break;
            }
            case CommonValue.WARN_LOG:{
                while (true){
                    if (!warnFileState){
                        boolean lock = warnLock.tryLock();
                        if (lock){
                            logger.info("lock for WarnLog.log successfully.");
                            break;
                        }
                    }
                    ThreadUtil.sleep(3);
                }
                break;
            }
            case CommonValue.USER_LOG:{
                while (true){
                    if (!userFileState){
                        boolean lock = userLock.tryLock();
                        if (lock){
                            logger.info("lock for UserLog.log successfully.");
                            break;
                        }
                    }
                    //获取失败3秒后再去尝试
                   ThreadUtil.sleep(3);
                }
                break;
            }
            default:{
                logger.error("file name is not right.");
                return;
            }
        }

        //写文件IO对象
        FileWriter writer=null;
        //写缓存
        BufferedWriter bufferedWriter=null;
        try {
            //fileName:warnLog.log  errorLog.log UserLog.log
            File logFile = new File(fileName);
            if (!logFile.exists()){
                logFile.createNewFile();
            }
            //*****************先写日志，写完后再判断是否超过上限***********************

            //追加写入日志信息
             writer = new FileWriter(logFile.getAbsoluteFile(), true);
             bufferedWriter = new BufferedWriter(writer);
             bufferedWriter.write(logContent);
            //换行
            bufferedWriter.newLine();
            bufferedWriter.flush();
            logger.info(fileName+" length:"+logFile.length()+" B");

            //判断日志是否超过上限
            SystemConfig config=secUserService.getStorageSystemConfig();
            Long count=Long.parseLong(config.getLogMaxVolume());
            //GB转换成B
            long maxVolume=(count*CommonValue.ONE_GB);
            if (logFile.length()>=maxVolume) {
                //达到上限
                if (logFile.getName().toLowerCase().contains("user")){
                    //业务日志相关
                    userFileState=true;
                }else if(logFile.getName().toLowerCase().contains("warn")){
                    //告警日志相关
                    warnFileState=true;
                }else {
                    //错误日志相关
                    errorFileState=true;
                }

                logger.info(fileName + " has arrived maxVolume:" + count + " GB;");
                ThreadPoolExecutor poolExecutor = ThreadUtil.getThreadPoolExecutor();
                poolExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        long start = System.currentTimeMillis();
                        logger.info("logFile automatic backup begin" );
                        backUpLogFileWithRenameTo(logFile);
                        logger.info("logFile automatic backup end,cost time:" +(System.currentTimeMillis()-start)/1000.0+"s");

                        //执行完毕修改对应文件状态
                        if (logFile.getName().toLowerCase().contains("user")){
                            //业务日志相关
                            userFileState=false;
                        }else if(logFile.getName().toLowerCase().contains("warn")){
                            //告警日志相关
                            warnFileState=false;
                        }else {
                            //错误日志相关
                            errorFileState=false;
                        }
                    }
                });
            }
        }catch (IOException e) {
            e.printStackTrace();
        }  finally {
                //肯定会执行
            try {
                bufferedWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            //释放锁
            switch (fileName){
                case CommonValue.ERROR_LOG:{
                    errorLock.unlock();
                    logger.info("unlock for ErrorLog.log successfully");
                    break;
                }
                case CommonValue.WARN_LOG:{
                    warnLock.unlock();
                    logger.info("unlock for WarnLog.log successfully");
                    break;
                }
                case CommonValue.USER_LOG:{
                    userLock.unlock();
                    logger.info("unlock for UserLog.log successfully");
                    break;
                }
            }
        }
    }

    private  void backUpLogFileWithRenameTo(File sourceFile){
        /**
        * @Description: 使用 java的RenameTo方法来实现备份,使用renameTo方法，隐患是不能在不同文件格式的磁盘上移动重命名，同一个磁盘可以
        * @Param: [fileNameWithSuffix]
        * @return: void
        * @Author: SongDaisong
        * @Date:  2020/4/16 15:17
        */

//        System.out.println(sourceFile.getName()+" 开始休眠-----模拟并发"+Thread.currentThread().getId());

        String sourceFileName = sourceFile.getName();
        //自动备份
        //UserLog-2020-4-15 16-31-20.log 备份日志命名
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH-mm-ss");
        LocalDateTime time = LocalDateTime.now();
        String suffix = df.format(time);

        String newName=sourceFileName.split("\\.")[0] + "-" + suffix + ".log";
        System.out.println("newName: "+newName);

        //备份文件名
        File file = new File(newName);

        boolean result = sourceFile.renameTo(file);   //执行成功原文件就不会存在
        logger.info(sourceFileName+" has automatic backup:"+(result?"success":"failed"));
        if(result){

            FileInputStream fileInputStream=null;

            OutputStream fileOutputStream=null;
            ZipOutputStream zipOutputStream=null;
            try {
                //renameTo()成功后，原文件会被删除
                sourceFile.createNewFile();
                //将备份文件压缩成zip
                //文件输入
                fileInputStream = new FileInputStream(file);
                //8MB大小
                byte[] buffer=new byte[8*1024*1024];
                //文件你输出
                fileOutputStream = new FileOutputStream(sourceFileName.split("\\.")[0] + "-" + suffix+".zip");
                zipOutputStream = new ZipOutputStream(fileOutputStream);
                ZipEntry entry = new ZipEntry(file.getName());
                zipOutputStream.putNextEntry(entry);
                int count=0;
                long start=System.currentTimeMillis();
                while ((count=fileInputStream.read(buffer))!=-1){
                    zipOutputStream.write(buffer,0,count);
                }
                logger.info("the compression time consuming is: "+(System.currentTimeMillis()-start)/1000.0+" s");
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    zipOutputStream.close();
                    fileOutputStream.close();
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //判断压缩文件是否成功，成功的话删除非压缩的文件
                if (new File(sourceFileName.split("\\.")[0] + "-" + suffix+".zip").exists()){
                    file.delete();
                }
            }
        }

//        try {
//            //1 s=1000 ms
//            Thread.sleep(20*1000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }finally {
//            System.out.println(sourceFile.getName()+" 结束休眠----模拟并发");
//        }

    }

    private  void backUpLogFile(File sourceFile){
        /** 
        * @Description: 读写形式进行本地日志备份
        * @Param: [fileName]
        * @return: void 
        * @Author: SongDaisong 
        * @Date:  2020/4/15 15:53
        */ 

        //每条记录大概：200B
        //1G=1024*1024*1024/200=5,368,709.12 五百多万条记录

        //fileName.log
        String[] split = sourceFile.getName().split("\\.");

        String fileName=split[0];

        File logBackUpfile=null;
        InputStream inputStream=null;
        BufferedReader bufferedReader=null;

        FileWriter fileWriter=null;
        BufferedWriter bufferedWriter=null;

        //每次读取一行
        String line=null;
        //缓冲区每写入两万行记录，flush一次
        AtomicInteger currBufLogs = new AtomicInteger(0);

        try {
         //UserLog-2020-4-15.log 备份日志命名
         logBackUpfile = new File(fileName+"-"+ LocalDate.now().toString()+".log");

        //文件输入流，读取原文件
         inputStream = new FileInputStream(fileName);
        //输入缓存
         bufferedReader=new BufferedReader(new InputStreamReader(inputStream));

        //原文件日志信息输出到备份文件里面
         fileWriter = new FileWriter(logBackUpfile, true);
        //输出缓存，缓存大小设置 4*1024*1024
         bufferedWriter = new BufferedWriter(fileWriter,(int)(4*CommonValue.ONE_MB));

        while ((line=bufferedReader.readLine())!=null){
                bufferedWriter.write(line);
                bufferedWriter.newLine();
                if (currBufLogs.incrementAndGet()>=20000){
                    bufferedWriter.flush();
                    currBufLogs.set(0);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //缓存刷新到磁盘
            try {
                bufferedWriter.flush();
                //流逆序关闭
                bufferedWriter.close();
                fileWriter.close();
                bufferedReader.close();
                inputStream.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

}
